package com.wuyuan.xy.interceptor;

import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

public class HttpClientUtil {

    private static CloseableHttpClient httpClient;

   // private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);

    //  客户端与服务器建立连接的超时时间,根据网络情况，内网、外网等，可设置连接超时时间为2秒，具体根据业务调整
    private static final int HTTP_CON_TIMEOUT = 3000;
    //  客户端从服务端读取数据的超时时间
    private static final int HTTP_TIMEOUT = 3000;
    //  客户端从连接池中获取连接的超时时间.建议设置500ms即可，不要设置太大，这样可以使连接池连接不够时不用等待太久去获取连接，不要让大量请求堆积在获取连接处，尽快抛出异常，发现问题。
    private static final int HTTP_CON_REQ_TIMEOUT = 800;
    //  路由的默认最大连接.默认是2，每个route（可以认为是一个域名，但是看它的equals方法，本地IP Address不同也不是一个route）只能建立两个HTTP连接(已经验证)
    private static final int HTTP_MAX_PERROUTE = 500;
    //  整个连接池连接的最大值
    private static final int HTTP_MAX_TOTAL = 1000;
    // private static RequestConfig defaultRequestConfig = null;


    /**
     * 信任SSL证书
     */
    static {
        HttpClientBuilder builder = HttpClientBuilder.create();
        //  创建连接池管理器
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        //  设置socket配置
        SocketConfig socketConfig = SocketConfig.custom()
                .setTcpNoDelay(true)
                .build();
        connManager.setDefaultSocketConfig(socketConfig);
        connManager.setMaxTotal(HTTP_MAX_TOTAL);
        connManager.setDefaultMaxPerRoute(HTTP_MAX_PERROUTE);
        // 设置超时时间
        RequestConfig config = RequestConfig.custom().
                setConnectTimeout(HTTP_CON_TIMEOUT).
                setSocketTimeout(HTTP_TIMEOUT).
                setConnectionRequestTimeout(HTTP_CON_REQ_TIMEOUT)
                .build();
        //想用连接池了，但是还是有SSL证书的问题。  https://blog.csdn.net/qq_36908872/article/details/127072270这上面的文章配置不对，没时间看了
       // builder.setConnectionManager(connManager);
        builder.setDefaultRequestConfig(config);

        /****************************
         2024年3月18日 LQ
         中间这段代码是使用连接池，但是为了防止出现各种问题，还是先不用连接池了。即PoolingHttpClientConnectionManager connManager 这个东西先没用
         //管理器是共享的，它的生命周期将由调用者管理，并且不会关闭
         //否则可能出现Connection pool shut down异常
         //    builder.setConnectionManager(connManager).setConnectionManagerShared(true);
         // 长连接策略
         //2024年3月18日 LQ DefaultConnectionKeepAliveStrategy不知道啥意思。试试把。
         //builder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
         ************************/

        // 忽略SSL证书验证
        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
        try {
            sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            });
            SSLContext sslContext = sslContextBuilder.build();
            builder.setSSLContext(sslContext);
            builder.setSSLHostnameVerifier(new NoopHostnameVerifier());
            httpClient = builder.build();
        } catch (Exception e) {
          //  logger.info("自定义HttpClientUtil信任证书的问题:" + e.getMessage());
            e.printStackTrace();

        }
/*2024年3月15日 LQ 因为有了SSL证书，所以试一下去掉下面的代码有没有问题
        try {

            SSLContext sslContext = SSLContextBuilder.create().useProtocol(SSLConnectionSocketFactory.SSL).loadTrustMaterial((x, y) -> true).build();
            //setMaxRedirects设置，发现没用，ConnectTimeout3秒的话有点长，改成两秒。
            RequestConfig config = RequestConfig.custom().setConnectTimeout(HTTP_CON_TIMEOUT).setSocketTimeout(HTTP_TIMEOUT).setConnectionRequestTimeout(HTTP_CON_REQ_TIMEOUT).build();
          //  .setSocketTimeout(5000)  .setConnectTimeout(5000)  .setConnectionRequestTimeout(5000)  .setStaleConnectionCheckEnabled(true) .setCircularRedirectsAllowed(true).setMaxRedirects(5) .build()
            httpClient = HttpClientBuilder.create().setConnectionManager(connManager).setDefaultRequestConfig(config).setSSLContext(sslContext).setSSLHostnameVerifier((x, y) -> true).build();
        } catch (Exception e) {
            logger.info("自定义HttpClientUtil信任证书的问题:"+e.getMessage());
            e.printStackTrace();

        }
 */

    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String doPost(String url, Map<String, Object> params, Map<String, Object> headers) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        String aa = null;
        CloseableHttpResponse response = null;
        try {
            //创建HttpPost
            HttpPost httpPost = new HttpPost(url);
            //添加params
            JSONObject jsonObject = new JSONObject();
            if (params != null && !params.isEmpty()) {
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    String value = entry.getValue().toString();
                    if (value != null) {
                        jsonObject.put(entry.getKey(), value);
                    }
                }
            }
            aa = jsonObject.toString();
            StringEntity s = new StringEntity(jsonObject.toString(), "utf-8");
            s.setContentEncoding("UTF-8");
            s.setContentType("application/json");//发送json数据需要设置contentType
            // 将请求实体设置到httpPost对象中
            httpPost.setEntity(s);

            //添加header
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, Object> entry : headers.entrySet()) {
                    String value = entry.getValue().toString();
                    if (value != null) {
                        String cc = entry.getKey().toString();
                        httpPost.addHeader(entry.getKey().toString(), value);
                    }
                }
            }
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpPost.abort();
                //2024年3月14日 LQ 先不throw试试。
                //  throw new RuntimeException("HttpClient is error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
          //  logger.error("doPost    " + url + "     " + aa + "     " + e.getMessage());
            // e.printStackTrace();
            try {
                response.close();
            } catch (Exception ex) {
               // logger.error("doPost    " + url + "     " + aa + "     " + ex.getMessage());
                // ex.printStackTrace();
            }
        }
        return null;
    }


    public static String doPostJsonStringParm(String url, String jsonParams, Map<String, Object> headers) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        CloseableHttpResponse response = null;
        try {
            //创建HttpPost
            HttpPost httpPost = new HttpPost(url);
            //添加params
            StringEntity s = new StringEntity(jsonParams, "utf-8");
            s.setContentEncoding("UTF-8");
            s.setContentType("application/json");//发送json数据需要设置contentType
            // 将请求实体设置到httpPost对象中
            httpPost.setEntity(s);
            //添加header
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, Object> entry : headers.entrySet()) {
                    String value = entry.getValue().toString();
                    if (value != null) {
                        String cc = entry.getKey().toString();
                        httpPost.addHeader(entry.getKey().toString(), value);
                    }
                }
            }
            //2024年3月13日 LQ 给象屿的中文乱码，因此加入下面两个header试试。
            httpPost.setHeader("content-Type", "application/json;charset=utf-8");
            //2024年3月19日 试试加入keep-alive，能不能解决卡的问题
            httpPost.setHeader("Connection", "keep-alive");
            httpPost.setHeader("Accept", "application/json");
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpPost.abort();
                //加一个return
                return null;
                //2024年3月14日 LQ 先不throw试试。
                //  throw new RuntimeException("HttpClient is error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
            // e.printStackTrace();
          //  logger.error("doPostJsonStringParm     " + url + "    " + jsonParams + "     " + e.getMessage());
            try {
                response.close();
            } catch (Exception ex) {
               // logger.error("doPostJsonStringParm     " + url + "     " + jsonParams + "     " + ex.getMessage());
            }
        }
        return null;
    }


    /**
     * post请求 发送json格式的报文 StringEntity
     *
     * @param url
     * @param jsonString
     * @return
     */
    public static String doPost(String url, String jsonString) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(jsonString, "utf-8");
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            CloseableHttpResponse response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpPost.abort();

                //  throw new RuntimeException("HttpClient is error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
            //  e.printStackTrace();
        }
        return null;
    }

    //post请求，没有参数
    public static String doPostNoparams(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = new HttpPost(url);

            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpPost.abort();
                //2024年3月14日 LQ 先不throw试试。
                // throw new RuntimeException("HttpClient is error status code :"+ statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
          //  logger.error(" doPostNoparams    " + url + e.getMessage());
            //e.printStackTrace();

        } finally {

            if (response != null) {
                try {
                    response.close();
                } catch (Exception e) {
                   // logger.error(" doPostNoparams    " + url + e.getMessage());
                    // e.printStackTrace();

                }

            }

        }
        return null;
    }


    /**
     * post请求
     *
     * @param url
     * @param
     * @return
     */
    public static String doGet(String url, Map<String, Object> headers) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        CloseableHttpResponse response = null;
        try {
            //创建HttpPost
            HttpGet httpGet = new HttpGet(url);
            //添加header
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, Object> entry : headers.entrySet()) {
                    String value = entry.getValue().toString();
                    if (value != null) {
                        String cc = entry.getKey().toString();
                        httpGet.addHeader(entry.getKey().toString(), value);
                    }
                }
            }
            response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpGet.abort();

                //  throw new RuntimeException("HttpClient is error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
          //  logger.error(" doGet    " + url + e.getMessage());
            // e.printStackTrace();
            try {
                response.close();
            } catch (Exception ex) {
                //ex.printStackTrace();
            }
        }
        return null;
    }
}
